package com.hk.mybatis.query;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hk.mybatis.core.ColumnResolver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 动态条件公共类
 */
@Slf4j
public class DynamicQueryWrapperUtil {

    /**
     * 创建查询规则
     *
     * @param queryOptions 查询条件
     */
    public static <T> QueryWrapper<T> buildQueryWrapper(final QueryOptions queryOptions, Class<T> cls) {
        ColumnResolver<T> columnResolver = new ColumnResolver<>();
        if (queryOptions == null || queryOptions.getConditionGroup() == null) {
            return new QueryWrapper<>();
        }
        QueryWrapper<T> root = new QueryWrapper<>();
        if ((queryOptions.getConditionGroup().getConditions() == null || queryOptions.getConditionGroup().getConditions().isEmpty()) && (queryOptions.getConditionGroup().getSubConditionGroups() == null || queryOptions.getConditionGroup().getSubConditionGroups().isEmpty())) {
            if (!queryOptions.getOrderBys().isEmpty()) {
                setOrders(queryOptions.getOrderBys(), cls, columnResolver, root);
            }
            return root;
        }
        root.and(tQueryWrapper -> buildQueryWrapper(queryOptions.getConditionGroup(), tQueryWrapper, columnResolver, cls));
        if (!queryOptions.getOrderBys().isEmpty()) {
            setOrders(queryOptions.getOrderBys(), cls, columnResolver, root);
        }
        return root;
    }

    private static <T> void setOrders(List<OrderBy> orders, Class<T> cls, ColumnResolver<T> columnResolver, QueryWrapper<T> root) {
        for (OrderBy orderBy : orders) {
            String queryColumn = orderBy.getProperty();
            String resolveColumn = columnResolver.resolve(queryColumn, cls);
            if (resolveColumn != null) {
                queryColumn = resolveColumn;
            } else {
                queryColumn = parseColumn(queryColumn);
            }
            root.orderBy(true, orderBy.isAsc(), queryColumn);
        }
    }

    /**
     * 创建查询规则
     *
     * @param conditionGroup 查询条件分组
     */
    public static <T> QueryWrapper<T> buildQueryWrapper(final ConditionGroup conditionGroup, Class<T> cls) {
        if (conditionGroup.getConditions().isEmpty() && conditionGroup.getSubConditionGroups().isEmpty()) {
            return null;
        }
        QueryWrapper<T> root = new QueryWrapper<>();
        ColumnResolver<T> columnResolver = new ColumnResolver<>();
        root.and(tQueryWrapper -> buildQueryWrapper(conditionGroup, tQueryWrapper, columnResolver, cls));
        return root;
    }

    /**
     * 创建查询规则
     *
     * @param conditionGroup 查询条件分组
     */
    private static <T> void buildQueryWrapper(final ConditionGroup conditionGroup, QueryWrapper<T> queryWrapper, ColumnResolver<T> columnResolver, Class<T> cls) {
        List<QueryCondition> conditions = conditionGroup.getConditions();
        List<ConditionGroup> subConditionGroups = conditionGroup.getSubConditionGroups();
        String queryRelation = conditionGroup.getQueryRelation();

        if (!CollectionUtils.isEmpty(conditions)) {
            for (QueryCondition queryCondition : conditions) {
                String queryColumn = queryCondition.getProperty();
                log.debug("分析条件... {}", queryColumn);
                if (ObjectUtils.isEmpty(queryCondition.getValue())) {
                    if (!queryCondition.getOperator().equalsIgnoreCase(Operator.BETWEEN) && !queryCondition.getOperator().equalsIgnoreCase(Operator.ISNULL) && !queryCondition.getOperator().equalsIgnoreCase(Operator.ISNOTNULL)) {
                        break;
                    }
                }
                log.debug("判断条件: {}", queryCondition.getOperator().toUpperCase());
                // 转日期
                Date valueDate = parseDate(queryCondition.getValue());
                Date beginDate = parseDate(queryCondition.getBegin());
                Date endDate = parseDate(queryCondition.getEnd());

                Object value = valueDate == null ? queryCondition.getValue() : valueDate;
                Object begin = beginDate == null ? queryCondition.getBegin() : beginDate;
                Object end = endDate == null ? queryCondition.getEnd() : endDate;
                if (queryCondition.isFk()) {
                    queryColumn = parseColumn(queryColumn);
                } else {
                    String resolveColumn = columnResolver.resolve(queryColumn, cls);
                    if (resolveColumn != null) {
                        queryColumn = resolveColumn;
                    } else {
                        queryColumn = parseColumn(queryColumn);
                    }
                }

                switch (queryCondition.getOperator().toUpperCase()) {
                    case Operator.EQ:
                        log.debug("CASE: EQ");
                        queryWrapper.eq(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.NEQ:
                        log.debug("CASE: NEQ");
                        queryWrapper.ne(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.LIKE:
                        log.debug("CASE: LIKE");
                        queryWrapper.like(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.LIKERIGHT:
                        log.debug("CASE: LIKERIGHT");
                        queryWrapper.likeRight(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.LIKELEFT:
                        log.debug("CASE: LIKELEFT");
                        queryWrapper.likeLeft(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.NLIKE:
                        log.debug("CASE: NLIKE");
                        queryWrapper.notLike(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.GT:
                        log.debug("CASE: GT");
                        queryWrapper.gt(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.LT:
                        log.debug("CASE: LT");
                        queryWrapper.lt(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.GTE:
                        log.debug("CASE: GTE");
                        queryWrapper.ge(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.LTE:
                        log.debug("CASE: LTE");
                        queryWrapper.le(queryColumn, queryCondition.getValue());
                        break;
                    case Operator.BETWEEN:
                        log.debug("CASE: BETWEEN");
                        queryWrapper.between(queryColumn, begin, end);
                        break;
                    case Operator.ISNULL:
                        log.debug("CASE: ISNULL");
                        queryWrapper.isNull(queryColumn);
                        break;
                    case Operator.ISNOTNULL:
                        log.debug("CASE: ISNOTNULL");
                        queryWrapper.isNotNull(queryColumn);
                        break;
                    case Operator.IN:
                        log.debug("CASE: IN");
                        queryWrapper.in(queryColumn, parseList(value));
                        break;
                    case Operator.NOTIN:
                        log.debug("CASE: NOTIN");
                        queryWrapper.notIn(queryColumn, parseList(value));
                        break;
                    case Operator.CONTAINS:
                        log.debug("CASE: CONTAINS");
                        queryWrapper.apply("{0}=ANY(" + queryColumn + ")", value);
                        break;
                    default:
                        throw new IllegalArgumentException(String.format("不支持'%s'条件", queryCondition.getOperator()));
                }
                log.debug("查询关系:{}", queryRelation.toUpperCase());
                switch (queryRelation.toUpperCase()) {
                    case QueryRelation.OR:
                        queryWrapper = queryWrapper.or();
                        break;
                    case QueryRelation.AND:
                        break;
                    default:
                        throw new IllegalArgumentException(String.format("不支持'%s'逻辑条件", queryRelation));
                }
            }
        }

        if (!CollectionUtils.isEmpty(subConditionGroups)) {
            for (ConditionGroup subConditionGroup : subConditionGroups) {
                switch (queryRelation.toUpperCase()) {
                    case QueryRelation.OR:
                        queryWrapper.or(tQueryWrapper -> buildQueryWrapper(subConditionGroup, tQueryWrapper, columnResolver, cls));
                        break;
                    case QueryRelation.AND:
                        queryWrapper.and(tQueryWrapper -> buildQueryWrapper(subConditionGroup, tQueryWrapper, columnResolver, cls));
                        break;
                    default:
                        throw new IllegalArgumentException(String.format("不支持'%s'逻辑条件", queryRelation));
                }
            }
        }
    }

    /**
     * 对象转List
     *
     * @param object 被转换对象
     */
    private static List<?> parseList(Object object) {
        List<Object> result = new ArrayList<>();
        if (object != null) {
            if (object instanceof List) {
                result.addAll((List<?>) object);
            } else {
                result.add(object);
            }
        }
        return result;
    }

    /**
     * 对象转日期
     *
     * @param object 被转换对象
     * @return 转换后日期，转换失败返回null
     */
    public static Date parseDate(Object object) {
        String[] formats = new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"};
        Date date = null;
        // 不为空，并且是String的实例
        if (object instanceof String) {
            for (String format : formats) {
                SimpleDateFormat sdf = new SimpleDateFormat(format);

                // 禁止松散验证
                sdf.setLenient(false);
                try {
                    date = sdf.parse(object.toString());
                } catch (ParseException ignored) {
                }
            }
        }
        return date;
    }

    public static Pattern compile = Pattern.compile("[A-Z]");

    public static String parseColumn(String column) {
        Matcher matcher = compile.matcher(column);
        StringBuilder sb = new StringBuilder();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "." + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}
